package de.herberlin.sudoku.gui;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.io.Serializable;
import java.util.logging.Logger;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;

import de.herberlin.sudoku.Constants;
import de.herberlin.sudoku.Field;
import de.herberlin.sudoku.Main;
import de.herberlin.sudoku.Sudoku;
import de.herberlin.sudoku.timer.AnimationEvent;
import de.herberlin.sudoku.timer.AnimationListener;
import de.herberlin.sudoku.timer.Timesource;

public class ButtonField extends JButton implements Field, ActionListener,
		AnimationListener, Serializable {

	static final long serialVersionUID = 1L;

	private Sudoku sudoku = null;

	private int x = Constants.INT_NOT_SET;

	private int y = Constants.INT_NOT_SET;

	private boolean mutable = true;

	private boolean useResolvedColor = false;

	private boolean success=false;

	private int value = Constants.INT_NOT_SET;

	private int resolvedValue = value;

	public ButtonField(int x, int y) {
		super();
		this.x = x;
		this.y = y;
		addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				if (e.isPopupTrigger()) {
					showPopup(e);
					e.consume();
				}
			}

			public void mouseReleased(MouseEvent e) {
				if (e.isPopupTrigger()) {
					showPopup(e);
				} else {
					updateValue();
				}
			}
		});
		Timesource.getInstance().addListener(this);
		doPaint();
	}

	private JPopupMenu popup = null;

	private void showPopup(MouseEvent e) {
		if (isMutable()) {
			if (popup == null) {
				popup = new JPopupMenu();
				int max = getSudoku().getRadix() * getSudoku().getRadix();
				JMenuItem test = new JMenuItem("Test");
				test.addActionListener(this);
				test.setActionCommand("test");
				popup.add(test);

				JMenuItem remove = new JMenuItem("-");
				remove.addActionListener(this);
				remove.setActionCommand("remove");
				popup.add(remove);

				popup.addSeparator();

				for (int i = 0; i < max; i++) {
					JMenuItem item = new JMenuItem("" + (i + 1));
					item.addActionListener(this);
					item.setActionCommand("" + i);
					popup.add(item);
				}
			}

			popup.show(this, e.getX(), e.getY());
		}// ~ mutable
	}

	private void doPaint() {

		DesignFactory design = DesignFactory.getInstance();
		setContentAreaFilled(!design.isTransparent());
		setOpaque(!design.isTransparent());
		this.setBorder(BorderFactory.createEtchedBorder());

		setFont(design.getButtonFont());

		blink--;
		blink = Math.max(0, blink);
		// logger.fine("Blink="+blink);

		// find text
		if (getValue() == Constants.INT_NOT_SET || blink % 2 != 0) {
			this.setText(" ");
		} else {
			this.setText("" + getValue());
		}

		// find color
		if (useResolvedColor) {
			this.setForeground(design.getResolvedColor());
		} else if (isMutable()) {
			this.setForeground(design.getEnabledColor());
		} else {
			this.setSelected(false);
			this.setForeground(design.getDisabledColor());
		}

	}

	private transient Logger logger = Logger.getLogger(getClass().getName());

	public void actionPerformed(ActionEvent e) {

		logger.fine(e + "");

		if ("test".equals(e.getActionCommand())) {
			updateConflictFields(6);
		} else if ("remove".equals(e.getActionCommand())) {
			updateConflictFields(0);
			value = -1;
		} else {
			value = Integer.parseInt(e.getActionCommand());
			updateValue();
		}

	}

	private void updateValue() {
		if (isMutable()&& !success) {
			int radix = getSudoku().getRadix();
			if (value == Constants.INT_NOT_SET) {
				value = 1;
			} else if (value < radix * radix) {
				value = value + 1;
			} else {
				value = Constants.INT_NOT_SET;
			}
			updateConflictFields(0);
			conflictFields = sudoku.getConflictFields(this);
			updateConflictFields(6);
		}
	}

	private Field[] conflictFields = null;

	private void updateConflictFields(int value) {
		if (conflictFields != null) {
			for (int i = 0; i < conflictFields.length; i++) {
				((ButtonField) conflictFields[i]).setBlink(value);
			}
		} else if (sudoku.isResolved() && !success) {
			JOptionPane.showMessageDialog(this, Main
					.getMessage("message.success"));
			Field[][] fields = sudoku.getFields();
			for (int m = 0; m < fields.length; m++) {
				for (int n = 0; n < fields[m].length; n++) {
					fields[m][n].setSuccess();
				}
			}
		}
	}

	public Sudoku getSudoku() {
		return sudoku;
	}

	public void setSudoku(Sudoku sudoku) {
		this.sudoku = sudoku;
	}

	public int getValue() {
		return value;
	}

	public void setValue(int currentValue) {
		this.value = currentValue;
		if (value != Constants.INT_NOT_SET) {
			resolvedValue = value;
		}
		doPaint();

	}

	public boolean isMutable() {
		return mutable;
	}

	public void setMutable(boolean modifiable) {
		this.mutable = modifiable;
		doPaint();
	}

	public int getXPos() {
		return x;
	}

	public int getYPos() {
		return y;
	}

	public String toString() {
		return "[" + x + "," + y + "], value=" + getValue() + ", mutable="
				+ isMutable();
	}

	public Dimension getPreferredSize() {

		int s = DesignFactory.getInstance().getButtonSize();
		return new Dimension(s, s);
		// Dimension dim = super.getPreferredSize();
		// dim.width = dim.height;
		// return dim;
	}

	public void time(AnimationEvent event) {
		// logger.fine("Event="+event);
		doPaint();
	}

	private int blink = 0;

	public void setBlink(int blink) {
		this.blink = blink;
		doPaint();
	}

	public void setResolved() {
		value = resolvedValue;
		if (mutable) {
			this.useResolvedColor = true;
			mutable = false;
		}
		doPaint();
	}

	private void readObject(java.io.ObjectInputStream stream)
			throws IOException, ClassNotFoundException {

		stream.defaultReadObject();
		removeActionListener(this);
		addActionListener(this);
		Timesource.getInstance().addListener(this);
		doPaint();

	}

	private void writeObject(java.io.ObjectOutputStream stream)
			throws IOException {

		stream.defaultWriteObject();
	}

	public void setSuccess() {
		this.success = true;
	}

}
